package cc.rengu.igas.mcps.core.service.base;

import cc.rengu.igas.mcps.common.constant.McpsParamConstant;
import cc.rengu.igas.mcps.common.constant.McpsTreeNodeConstant;
import cc.rengu.igas.mcps.common.constant.McpsTxnNumConstant;
import cc.rengu.igas.mcps.common.enums.RespCodeEnum;
import cc.rengu.igas.mcps.core.realize.PosPublicCheckService;
import cc.rengu.igas.mcps.core.realize.impl.PosPublicCheckServiceImpl;
import cc.rengu.igas.share.common.constant.TransCacheConstant;
import cc.rengu.igas.share.core.model.MchntFeeInfo;
import cc.rengu.igas.share.core.model.MchntInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.igas.share.core.realize.impl.RiskMonitorServiceImpl;
import cc.rengu.oltp.service.base.BizBaseService;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.MchntOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.PayOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.UserOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.MchntOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.PayOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.UserOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.MchntOrderInfo;
import cc.rengu.oltp.service.common.entity.PayOrderInfo;
import cc.rengu.oltp.service.common.entity.SysParam;
import cc.rengu.oltp.service.common.entity.UserOrderInfo;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.model.OrderInfo;
import cc.rengu.oltp.service.model.RiskMonitorInfo;
import cc.rengu.oltp.service.realize.RiskMonitorService;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.annotation.BIZ_AFTER;
import cc.rengu.oltp.utility.annotation.BIZ_BEFORE;
import cc.rengu.oltp.utility.annotation.BIZ_PROCESS;
import cc.rengu.oltp.utility.util.*;
import com.alibaba.fastjson.JSON;

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * C2B预授权服务基类
 * Created by 王成 on 2018/4/17.
 */
public abstract class C2BAuthService extends BizBaseService {
    @Override
    protected Object beforeBizProcess() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String mchntNo = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.MCHNT_NO);
        String termNo = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.TERM_NO);
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String transAmt = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.TRANS_AMT);

        /* 金额不允许出现负数金额及0金额 */
        if (!StringUtil.isEmptyOrNull(transAmt) && AmountUtil.compare(transAmt, "0") <= 0) {
            rglog.error("金额不合法,{}", transAmt);
            throw new BizException(RespCodeEnum.REQUEST_ERROR.getRespCode(), RespCodeEnum.REQUEST_ERROR.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.ORDER_AMT, transAmt);

        /* Step1：查找BIZ_BEFORE的注解*/
        invokeBizAnnoMethod(BIZ_BEFORE.class);

        /* Step2：金融类交易公共校验 */
        PosPublicCheckService posPublicCheckService = new PosPublicCheckServiceImpl();
        /* Step2.1：mac验签，地理位置校验，sn校验，签到校验 */
        posPublicCheckService.posPublicCheckService();
        /* Step2.2：商户和终端的业务权限校验 */
        posPublicCheckService.posPublicAuthCtrlCheckService(txnInfo);

        /* Step3：获取商户基本信息 */
        MchntService mchntService = new MchntServiceImpl();
        MchntInfo mchntInfo = mchntService.getMchntInfo(instId, mchntNo);
        if (null == mchntInfo) {
            rglog.error("查询商户信息失败<{}>", mchntNo);
            throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
        }

        /* Step4：基于交易类型进行业务检查 */
        String txnNum = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.TXN_NUM);
        if (McpsTxnNumConstant.QR_AUTH_CANCEL_TXN_NUM.equals(txnNum) || McpsTxnNumConstant.QR_AUTH_COMP_CANCEL_TXN_NUM.equals(txnNum) || McpsTxnNumConstant.QR_AUTH_REVERSAL_TXN_NUM.equals(txnNum) ||
                McpsTxnNumConstant.QR_AUTH_CANCEL_REVERSAL_TXN_NUM.equals(txnNum) || McpsTxnNumConstant.QR_AUTH_COMP_REVERSAL_TXN_NUM.equals(txnNum) ||
                McpsTxnNumConstant.QR_AUTH_COMP_CANCEL_REVERSAL_TXN_NUM.equals(txnNum) || McpsTxnNumConstant.QR_AUTH_COMP_TXN_NUM.equals(txnNum)){
            String origTraceNo = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.ORI_TRACE_NO);
            String origPayId = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.ORI_PAY_ID);
            OrderInfo origOrderInfo = checkOrigOrderInfo(origTraceNo,origPayId);
            UserOrderInfo origUserOrderInfo = origOrderInfo.getUserOrderInfo();
            MchntOrderInfo origMchntOrderInfo = origOrderInfo.getMchntOrderInfoList().get(0);
            Optional<PayOrderInfo> payOrderInfoOpt = origOrderInfo.getPayOrderInfoList().stream().filter(item ->
                    item.getMsgDstId().equals(ChannelEnum.UPAL.getChannelType()) || item.getMsgDstId().equals(ChannelEnum.NUAL.getChannelType()) ||
                    item.getMsgDstId().equals(ChannelEnum.ALIP.getChannelType()) || item.getMsgDstId().equals(ChannelEnum.UPQC.getChannelType())).findFirst();
            PayOrderInfo origPayOrderInfo = payOrderInfoOpt.orElse(null);
            GlobalTransUtil.setCacheValue(TransCacheConstant.ORIG_USER_ORDER_INFO,origUserOrderInfo);
            GlobalTransUtil.setCacheValue(TransCacheConstant.ORIG_MCHNT_ORDER_INFO,origMchntOrderInfo);
            GlobalTransUtil.setCacheValue(TransCacheConstant.ORIG_PAY_ORDER_INFO,origPayOrderInfo);
            /* 判断是否为同一终端 */
            if (!(origMchntOrderInfo.getTermNo()).equals(termNo)) {
                rglog.error("根据原商户订单查询原终端号<{}>与此次交易终端号<{}>不一致，拒绝交易！", origOrderInfo.getMchntOrderInfoList().get(0).getTermNo(),termNo);
                throw new BizException(RespCodeEnum.POS_INVALID_REL_TXN.getRespCode(), RespCodeEnum.POS_INVALID_REL_TXN.getRespDesc());
            }
            /* 预授权完成交易校验 */
            if (McpsTxnNumConstant.QR_AUTH_COMP_TXN_NUM.equals(txnNum)) {
                /* 判断预授权完成是否超出时限 */
                String earDate = DateUtil.getFutureDay(-30);
                if (AmountUtil.compare(earDate, origPayOrderInfo.getTxnDate()) == 1) {
                    rglog.error("检查原交易信息,预授权完成日期超出时限！！orgTxnDate[{}]", origPayOrderInfo.getTxnDate());
                    throw new BizException(RespCodeEnum.POS_RSP_TIME_OUT.getRespCode(), RespCodeEnum.POS_RSP_TIME_OUT.getRespDesc());
                }
                /* 预授权金额大于等于预授权完成金额,单位为元 */
                BigDecimal multiplyAmt = new BigDecimal(origUserOrderInfo.getOrderAmt()).multiply(new BigDecimal("1.15"));
                String multiply = multiplyAmt.toString();
                if (!(multiply.compareTo(xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.TRANS_AMT)) >= 0)) {
                    rglog.error("根据原用户订单查询原预授权金额<{}> *1.15倍金额小于与此次交易预授权金额<{}>，拒绝交易！", origUserOrderInfo.getOrderAmt(), xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.TRANS_AMT));
                    throw new BizException(RespCodeEnum.POS_INVALID_AMT.getRespCode(), RespCodeEnum.POS_INVALID_AMT.getRespDesc());
                }
                /* 获取商户费率信息 */
                getMchntFeeInfo(mchntInfo);
            }
        }

        /* Step5：交易处理前客户化 */
        return bizBeforeCust();
    }

    @Override
    protected BizResponse callBizService(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        /* Step1：设置订单默认有效时间 */
        String orderLife = xmlTreeUtil.getXmlTreeStringValue(McpsParamConstant.ORDER_EXPIRY_TIME);
        if (StringUtil.isEmptyOrNull(orderLife)) {
            SysParamService sysParamService = new SysParamServiceImpl();
            SysParam sysParam = sysParamService.getSysParamInfo(instId, McpsParamConstant.NORMAL_TXNKEY_PREFIX, McpsParamConstant.ORDER_LIFE);
            /* Step3.1：若未配置订单有效期，则默认为180秒 */
            orderLife = null == sysParam ? "180" : sysParam.getParamValue().trim();
            xmlTreeUtil.setXmlTreeStringValue(McpsParamConstant.ORDER_EXPIRY_TIME, orderLife);
        }
        /* Step2：查找BIZ_PROCESS的注解 */
        invokeBizAnnoMethod(BIZ_PROCESS.class);
        /* Step3：业务逻辑处理*/
        return bizProcessCust(request);
    }

    @Override
    protected void afterBizProcess(BizResponse response) throws Exception {
        /* Step2：查找BIZ_AFTER的注解 */
        invokeBizAnnoMethod(BIZ_AFTER.class);
        /* Step3：业务调用后客户化处理 */
        bizAfterCust(response);
    }

    @Override
    protected RiskMonitorInfo getRiskMonitorInfo() {
        RiskMonitorInfo riskMonitorInfo = new RiskMonitorInfo();
        RiskMonitorService riskMonitorService = new RiskMonitorServiceImpl();
        riskMonitorInfo.setTransMonitor(riskMonitorService);
        return riskMonitorInfo;
    }

    /**
     * 业务处理前客户化处理
     *
     * @return 业务处理实体类
     */
    protected abstract Object bizBeforeCust() throws Exception;

    /**
     * 业务逻辑处理客户化处理
     *
     * @param request 业务处理前的返回实体类
     * @return 业务处理结果
     */
    protected abstract BizResponse bizProcessCust(Object request) throws Exception;

    /**
     * 业务处理后逻辑处理
     *
     * @param response 业务调用响应信息
     */
    protected abstract void bizAfterCust(Object response) throws Exception;

    /**
     * 获取商户费率信息
     * @param mchntInfo 商户信息
     * @throws Exception 异常
     */
    private void getMchntFeeInfo(MchntInfo mchntInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        /* Step1：获取商户的费率信息:非独立清算获取一级商户费率信息 */
        String mchntNo = mchntInfo.getMchntNo();
        if (AppParamConstant.NO.equals(mchntInfo.getIndependentDataFlag()) && "11".equals(mchntInfo.getMchntType())) {
            mchntNo = mchntInfo.getChainMchntNo();
        }
        MchntService mchntService = new MchntServiceImpl();
        List<MchntFeeInfo> mchntFeeInfoList = mchntService.getMchntFeeInfo(mchntInfo.getInstId(), mchntNo);
        if (null == mchntFeeInfoList || mchntFeeInfoList.isEmpty()) {
            rglog.error("获取商户<{}>扣率失败!", mchntNo);
            throw new BizException(RespCodeEnum.MCHNT_FREE_NOT_EXIST.getRespCode(), RespCodeEnum.MCHNT_FREE_NOT_EXIST.getRespDesc());
        }
        Optional<MchntFeeInfo> mchntFeeInfoOpt = mchntFeeInfoList.stream().filter(item -> item.getFeeInputMode().equals("0")).findFirst();
        MchntFeeInfo mchntFeeInfo;
        /* Step2：判断是否为特殊费率商户 */
        if (mchntFeeInfoOpt.isPresent()) {
            mchntFeeInfo = mchntFeeInfoOpt.get();
            if ("02".equals(txnInfo.getSrcTxnAuthCfg().getBizType())) {
                /* Step2.2.1：区分借贷记卡，条码第三位可以区分借贷类型，如果数值小于4借记卡，数值大于4是贷记卡 */
                String qrCode = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.QR_AUTH_CODE);
                if (AmountUtil.compare(qrCode.substring(2, 3), "4") == 1) {
                    xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.QR_CODE_TYPE, "C");
                } else {
                    xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.QR_CODE_TYPE, "D");
                }
            }
        } else {
            /* Step2.1：非特殊计费商户，过滤有效的费率记录 */
            mchntFeeInfoList = mchntFeeInfoList.stream().filter(item -> item.getFeeEffectiveDate().compareTo(DateUtil.getCurrentDate()) <= 0 &&
                    item.getFeeExpiryDate().compareTo(DateUtil.getCurrentDate()) >= 0).collect(Collectors.toList());
            /* Step2.2：根据业务细分区分是交易类型 */
            if (AppParamConstant.DEFAULT_BIZ_TYPE.equals(txnInfo.getSrcTxnAuthCfg().getBizType())) {
                /* Step2.2.1：微信条码预授权 */
                mchntFeeInfoOpt = mchntFeeInfoList.stream().filter(item -> "04".equals(item.getFeeType()) &&
                        item.getBaseProdCode().equals(txnInfo.getSrcTxnAuthCfg().getBizProdCode())).findFirst();
            } else if ("01".equals(txnInfo.getSrcTxnAuthCfg().getBizType())) {
                /* Step2.3.2：支付宝条码预授权 */
                mchntFeeInfoOpt = mchntFeeInfoList.stream().filter(item -> "05".equals(item.getFeeType()) &&
                        item.getBaseProdCode().equals(txnInfo.getSrcTxnAuthCfg().getBizProdCode())).findFirst();
            } else {
                /* Step2.3.3：银联条码预授权 */
                String qrCode = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.QR_AUTH_CODE);
                /* Step2.3.4：区分借贷记卡，条码第三位可以区分借贷类型，如果数值小于4借记卡，数值大于4是贷记卡 */
                if (!StringUtil.isEmptyOrNull(qrCode) && AmountUtil.compare(qrCode.substring(2, 3), "4") == 1) {
                    xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.QR_CODE_TYPE, "C");
                    mchntFeeInfoOpt = mchntFeeInfoList.stream().filter(item -> "03".equals(item.getFeeType()) &&
                            item.getBaseProdCode().equals(txnInfo.getSrcTxnAuthCfg().getBizProdCode())).findFirst();
                } else {
                    xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.QR_CODE_TYPE, "D");
                    mchntFeeInfoOpt = mchntFeeInfoList.stream().filter(item -> "13".equals(item.getFeeType()) &&
                            item.getBaseProdCode().equals(txnInfo.getSrcTxnAuthCfg().getBizProdCode())).findFirst();
                }
            }
            mchntFeeInfo = mchntFeeInfoOpt.orElse(mchntFeeInfoList.get(0));
        }
        rglog.info("商户付费率信息:<{}>", JSON.toJSONString(mchntFeeInfo));
        GlobalTransUtil.setCacheValue(TransCacheConstant.MCHNT_FEE_INFO, mchntFeeInfo);
    }

    /**
     * 获取原交易订单信息
     * @param origTraceNo 原请求流水号
     * @param origPayId 原支付流水号
     * @return 原订单信息
     * @throws Exception 异常
     */
    private OrderInfo checkOrigOrderInfo(String origTraceNo,String origPayId) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String msgSrcId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.MSG_SRC_ID);
        String transChannelId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_CHANNEL_ID);
        UserOrderInfo origUserOrderInfo = null;
        UserOrderInfoMapper userOrderInfoMapper = new UserOrderInfoMapperImpl();
        if (!StringUtil.isEmptyOrNull(origTraceNo)){
            origUserOrderInfo = userOrderInfoMapper.selectUserOrderInfoByFrontSeqNum(instId,origTraceNo,msgSrcId,transChannelId);
            if (null == origUserOrderInfo && !StringUtil.isEmptyOrNull(origPayId)){
                origUserOrderInfo = userOrderInfoMapper.selectUserOrderInfoByPrimaryKey(instId,origPayId);
            }
        }else if (!StringUtil.isEmptyOrNull(origPayId)){
            origUserOrderInfo = userOrderInfoMapper.selectUserOrderInfoByPrimaryKey(instId,origPayId);
        }
        if (null == origUserOrderInfo){
            rglog.error("获取原交易失败,origTraceNo:{},origPayId:{}",origTraceNo,origPayId);
            throw new BizException(RespCodeEnum.ORDER_NOEXIT.getRespCode(),RespCodeEnum.ORDER_NOEXIT.getRespDesc());
        }
        MchntOrderInfoMapper mchntOrderInfoMapper = new MchntOrderInfoMapperImpl();
        List<MchntOrderInfo> origMchntOrderInfoList = mchntOrderInfoMapper.selectMchntOrderInfoBySysSeqNum(instId,origUserOrderInfo.getSysSeqNum());
        PayOrderInfoMapper payOrderInfoMapper = new PayOrderInfoMapperImpl();
        List<PayOrderInfo> origPayOrderInfoList = payOrderInfoMapper.selectPayOrderInfoBySysSeqNum(instId,origUserOrderInfo.getSysSeqNum());
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserOrderInfo(origUserOrderInfo);
        orderInfo.setMchntOrderInfoList(origMchntOrderInfoList);
        orderInfo.setPayOrderInfoList(origPayOrderInfoList);
        return orderInfo;
    }
}